import json
import allure
import jsonpath

from json.decoder import JSONDecodeError
from common.debugtalk import DebugTalk
from common.read_yaml import get_testcase_yaml, ReadYamlData
from conf.openrationConfig import OperationConfig
from conf.setting import FILE_PATH
from common.sendrequests import SendRequests
from common.recordlog import logs
from common.assertions import Assertions


class RequestsBase(object):
    def __init__(self):
        self.read = ReadYamlData()
        self.conf = OperationConfig()
        self.send = SendRequests()
        self.assert_res = Assertions()

    def replace_load(self, data):
        """
        yaml文件替换解析有${}格式的数据
        :return:
        """
        str_data = data
        if not isinstance(data, str):
            str_data = json.dumps(data, ensure_ascii=False)

        for i in range(str_data.count('${')):
            if '${' in str_data and '}' in str_data:
                start_index = str_data.index('$')
                end_index = str_data.index('}', start_index)
                ref_all_params = str_data[start_index:end_index + 1]
                func_name = str_data[start_index + 2:str_data.index('(')]
                func_param = str_data[str_data.index('(') + 1:str_data.index(')')]
                # 传入替换的参数获取对应的值
                extract_data = getattr(DebugTalk(), func_name)(*func_param.split(',') if func_param else "")
                str_data = str_data.replace(ref_all_params, str(extract_data))

        if data and isinstance(data, (dict,list)):
            # if isinstance(data, dict):
                # logs.info(f'返回的数据为：{data}类型为字典')
            # if isinstance(data, list):
                # logs.info(f'返回的数据为：{data}类型为列表')
            data = json.loads(str_data)
        else:
            data = str_data
            # logs.info(f'返回的数据为：{data}类型为字符串')
        return data


    def specification_yaml(self, base_info, test_case):
        """
        规范yaml接口测试数据的写法
        :return:
        """
        try:
            cookie = None
            params_type = ['params', 'data', 'json']

            base_url = self.conf.get_section_for_data('api_envi', 'host')
            url = base_url + base_info['url']
            allure.attach(url, f'接口地址：{url}')

            api_name = base_info['api_name']
            allure.attach(api_name, f'接口名称：{api_name}')

            method = base_info['method']
            allure.attach(method, f'请求方法：{method}')

            header = self.replace_load(base_info['header'])
            allure.attach(str(header), f'请求头：{header}', allure.attachment_type.TEXT)

            if base_info.get('cookies') is not None:
                cookie = eval(self.replace_load(base_info['cookies']))
                # cookie = self.replace_load(base_info['cookies'])

            case_name = test_case.pop('case_name')
            allure.attach(case_name, f'测试用例名称：{case_name}')

            # 处理断言
            val = self.replace_load(test_case.get('validation'))
            test_case['validation'] = val

            # validation = eval(test_case.pop('validation'))
            validation = test_case.pop('validation')
            # 处理文件上传接口
            file, files = test_case.pop('files', None), None
            if file:
                for k, v in file.items():
                    allure.attach(json.dumps(file), '导入文件')
                    files = {k: open(v, mode='rb')}

            extract = test_case.pop('extract', None)
            extract_list = test_case.pop('extract_list', None)
            extract_test_case = test_case.pop('extract_test_case', None)
            for key, value in test_case.items():
                if key in params_type:
                    test_case[key] = self.replace_load(value)
            res = self.send.run_main(name=api_name,
                                     url=url,
                                     case_name=case_name,
                                     header=header,
                                     method=method,
                                     cookies=cookie,
                                     file=files,
                                     **test_case)

            allure.attach(res.text, f'接口返回信息：{res.text}', allure.attachment_type.TEXT)
            allure.attach(str(res.status_code), f'接口的状态码:{res.status_code}', allure.attachment_type.TEXT)

            try:
                res_json = json.loads(res.text)  # 把json格式转换成字典字典

                if extract:
                    self.extract_data(extract, res.text)
                if extract_list:
                    self.extract_data_list(extract_list, res.text)
                if extract_test_case:
                    self.extract_test_case_data(extract_test_case, test_case)
                # 处理接口断言
                # self.assert_res.assert_result(expected=validation, response=res.json(), status_code=res.status_code)
                self.assert_res.assert_result(expected=validation, response=res_json, status_code=res.status_code)
            except JSONDecodeError as js:
                logs.error('系统异常或接口未请求！')
                raise js
            except Exception as e:
                logs.error(e)
                raise e

        except Exception as e:
            logs.error(e)
            raise e


    def extract_data(self, testcase_extract, response):
        """
        提取接口的返回值，支持正则表达式提取以及json提取
        :param testcase_extract:yaml中的extract关键字
        :param response:接口的实际返回值
        :return:
        """
        try:
            for key, value in testcase_extract.items():
                # 处理json提取器
                if "$" in value:
                    ext_json = jsonpath.jsonpath(json.loads(response), value)[0]
                    if ext_json:
                        extract_data = {key: ext_json}
                    else:
                        extract_data = {key: None}
                    logs.info(f"json提取到的参数：{extract_data}")
                    self.read.write_yaml_data(extract_data)
        except:
            logs.error("提取extract参数失败")


    def extract_data_list(self, testcase_extract_list, response):
        """
        提取接口的返回值，支持正则表达式提取以及json提取
        :param testcase_extract_list:yaml中的extract_list关键字
        :param response:接口的实际返回值
        :return:
        """
        try:
            for key, value in testcase_extract_list.items():
                # 处理json提取器
                if "$" in value:
                    ext_json = jsonpath.jsonpath(json.loads(response), value)
                    if ext_json:
                        extract_data = {key: ext_json}
                    else:
                        extract_data = {key: None}
                    logs.info(f"json提取到的参数：{extract_data}")
                    self.read.write_yaml_data(extract_data)
        except:
            logs.error("提取extract_list参数失败")


    def extract_test_case_data(self, testcase_extract, testcase):
        try:
            for key, value in testcase_extract.items():
                extract_data = None
                if "$" in value:
                    # 兼容字典和字符串类型
                    if isinstance(testcase, dict):
                        json_data = testcase
                    else:
                        json_data = json.loads(testcase)

                    ext_json = jsonpath.jsonpath(json_data, value)
                    logs.info(f"json提取到的参数：{ext_json}")
                    if ext_json and ext_json is not False:
                        # 借鉴extract_data的处理方式，区分单值和列表
                        if isinstance(ext_json, list):
                            if len(ext_json) == 1:
                                extract_data = {key: ext_json[0]}  # 单元素列表取值
                            else:
                                extract_data = {key: ext_json}  # 多元素列表保留
                        else:
                            extract_data = {key: ext_json}  # 非列表直接使用
                    logs.info(f"json提取到的参数：{extract_data}")
                    self.read.write_yaml_data(extract_data)
        except Exception as e:
            logs.error(f"提取extract参数失败: {e}")
